/*
 * SPDX-FileCopyrightText: 2025 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

use anyhow::Result;
use log::{debug, warn};

use std::fs;

use std::path::PathBuf;

use crate::cli::ZipArgs;
use crate::error::ZipError;
use crate::utils::common::{
    copy_local_file_to_zip, copy_zip_file_to_zip, filter_filesystem_files,
    get_input_archive_comment, init_run_state, prepare_files_map, search_pattern_in_archive,
    set_file_comment, RunState,
};

use crate::commands::dif;

pub fn execute(args: &ZipArgs) -> Result<()> {
    if args.other.dif {
        dif::execute(args)?;
        return Ok(());
    }

    log::info!("Starting add operation...");

    validate_arguments(args)?;

    let mut run_state = init_run_state(args)?;

    let files_to_add = match prepare_files_map(args, &args.command, run_state.zip_file_tmp.as_ref())
    {
        Ok(files) => files,
        Err(e) => {
            // 检查是否需要在归档中搜索模式
            if e.to_string().contains("file_not_found:") {
                // 从错误消息中提取文件名模式
                let error_msg = e.to_string();
                if let Some(start) = error_msg.find("file_not_found: ") {
                    let pattern = &error_msg[start + "file_not_found: ".len()..];

                    // 检查zip文件是否存在
                    if let Some(zipfile) = args.zipfile.as_ref() {
                        if zipfile.exists() {
                            // zip文件存在，在归档中搜索匹配的模式
                            search_pattern_in_archive(&mut run_state, pattern, args)?
                        } else {
                            // zip文件不存在，显示"name not matched"警告
                            if !args.basic_options.quiet {
                                println!("        utzip warning: name not matched: {}", pattern);
                            }
                            return Err(ZipError::NothingToDo(zipfile.display().to_string()).into());
                        }
                    } else {
                        return Err(ZipError::InvalidArguments(
                            "No zip file specified".to_string(),
                        )
                        .into());
                    }
                } else {
                    return Err(e);
                }
            } else {
                return Err(e);
            }
        }
    };
    run_state.debug_print("Scanning files");
    run_state.debug_print("Applying filters");
    let filter_to_add = filter_filesystem_files(&files_to_add, args);
    run_state.debug_print("Checking dups");
    run_state.debug_print("Scanning files to update");
    run_state.debug_print(&format!("fcount = {}", filter_to_add.len()));

    let files_completed = add_files_to_zip(&mut run_state, &filter_to_add, args)?;

    finalize_zip(&mut run_state, files_completed, args)?;

    // 如果启用了-T参数且有文件要处理，压缩完成后执行测试
    // (单独的测试命令会在RunState::finish()中处理)
    if args.test.test && !args.files.is_empty() {
        crate::commands::test::test_zip_file(args)?;
    }

    Ok(())
}

fn add_files_to_zip(
    run_state: &mut RunState,
    files_to_add: &std::collections::BTreeMap<String, PathBuf>,
    args: &ZipArgs,
) -> Result<usize> {
    let mut files_completed = 0;
    debug!("Files to add: {:?}", files_to_add);
    if files_to_add.is_empty() {
        return Err(ZipError::NothingToDo("no match files".to_string()).into());
    }
    run_state.init_display_info(files_to_add, &args.command)?;

    // 处理 archive 和 writer 的所有权转移
    let mut writer = run_state.writer.take().expect("Writer not initialized");

    // 处理 archive 为空的情况
    if let Some(archive) = run_state.archive.take() {
        // 处理已有 ZIP 文件中的文件
        for i in 0..archive.len() {
            let file = match archive.by_index_raw(i) {
                Ok(f) => f,
                Err(e) => {
                    warn!("Failed to read file from archive: {}", e);
                    return Err(e);
                }
            };

            let name = file.name().to_string();
            if let Some(file_path) = files_to_add.get(&name) {
                debug!("updating file: {}", name);

                run_state.update_display_info(writer.get_current_split_index(), file_path);
                run_state.print_operation_start("updating", &name)?;

                // 检查是否是从zip条目中匹配的文件
                let file_path_str = file_path.to_string_lossy();
                if file_path_str.starts_with("__ZIP_ENTRY__:") {
                    // 这是从zip条目中匹配的文件，直接复制
                    if let Err(e) = copy_zip_file_to_zip(run_state, &file, &mut writer) {
                        warn!("Failed to copy file {}: {}", name, e);
                        return Err(e);
                    }
                    run_state.total_original_size += file.origin_size() as u64;
                    run_state.total_compressed_size += file.compressed_size() as u64;
                    run_state.changed_files.push(name.clone());
                    run_state.print_operation_end_args(
                        file.origin_size() as u64,
                        file.compressed_size() as u64,
                        file.compression_method(),
                    );
                    files_completed += 1;
                    continue;
                }

                match copy_local_file_to_zip(run_state, &mut writer, file_path, name.clone()) {
                    Ok(tracker) => {
                        run_state.total_original_size += tracker.original_size;
                        run_state.total_compressed_size += tracker.compressed_size;
                        run_state.changed_files.push(name.clone());

                        debug!("File {}: Original Size: {}, Compressed Size: {}, Compression Ratio: {:.2}%", name, tracker.original_size, tracker.compressed_size, tracker.ratio);
                        run_state.print_operation_end(&tracker);

                        files_completed += 1;
                        if args.basic_options.move_files {
                            // 如果是文件，直接删除
                            if file_path.is_file() {
                                debug!("delete original file {}", file_path.display());
                                if let Err(e) = fs::remove_file(file_path) {
                                    warn!(
                                        "Failed to delete original file {}: {}",
                                        file_path.display(),
                                        e
                                    );
                                    return Err(anyhow::Error::msg(format!(
                                        "Failed to delete original file {}: {}",
                                        file_path.display(),
                                        e
                                    )));
                                }
                            }
                            // 如果是目录，记录到待删除目录列表
                            else if file_path.is_dir() {
                                run_state.dirs_to_remove.insert(file_path.clone());
                            }
                        }
                    }
                    Err(e) => {
                        warn!("Failed to copy file {}: {}", name, e);
                        return Err(e);
                    }
                }
            } else {
                debug!("copy exits zip file: {}", name);

                if let Err(e) = copy_zip_file_to_zip(run_state, &file, &mut writer) {
                    warn!("Failed to copy file {}: {}", name, e);
                    return Err(e);
                }
            }
        }
        run_state.archive = Some(archive); // 将 archive 放回 run_state
    }

    // 处理新增文件（无论是否有原 archive）
    for (name, file_path) in files_to_add {
        // 跳过从zip条目中匹配的文件，它们已经在上面处理过了
        let file_path_str = file_path.to_string_lossy();
        if file_path_str.starts_with("__ZIP_ENTRY__:") {
            continue;
        }

        if run_state
            .archive
            .as_ref()
            .map_or(true, |a| !a.file_names().contains(name))
        {
            debug!("Adding new file: {}", name);
            run_state.update_display_info(writer.get_current_split_index(), file_path);
            run_state.print_operation_start("adding", name)?;
            match copy_local_file_to_zip(run_state, &mut writer, file_path, name.clone()) {
                Ok(tracker) => {
                    run_state.total_original_size += tracker.original_size;
                    run_state.total_compressed_size += tracker.compressed_size;
                    run_state.changed_files.push(name.clone());

                    debug!("File {}: Original Size: {}, Compressed Size: {}, Compression Ratio: {:.2}%", name, tracker.original_size, tracker.compressed_size, tracker.ratio);
                    run_state.print_operation_end(&tracker);

                    files_completed += 1;

                    if args.basic_options.move_files {
                        // 如果是文件，直接删除
                        if file_path.is_file() {
                            debug!("delete original file {}", file_path.display());
                            if let Err(e) = fs::remove_file(file_path) {
                                warn!(
                                    "Failed to delete original file {}: {}",
                                    file_path.display(),
                                    e
                                );
                                return Err(anyhow::Error::msg(format!(
                                    "Failed to delete original file {}: {}",
                                    file_path.display(),
                                    e
                                )));
                            }
                        }
                        // 如果是目录，记录到待删除目录列表
                        else if file_path.is_dir() {
                            run_state.dirs_to_remove.insert(file_path.clone());
                        }
                    }
                }
                Err(e) => {
                    warn!("Failed to copy file {}: {}", name, e);
                    return Err(e);
                }
            }
        }
    }

    run_state.writer = Some(writer);
    Ok(files_completed)
}

fn validate_arguments(args: &ZipArgs) -> Result<()> {
    if args.zipfile.is_none() {
        return Err(ZipError::InvalidArguments("No zip file specified".to_string()).into());
    }
    // if args.files.is_empty() {
    //     return Err(ZipError::InvalidArguments("No files specified to add".to_string()).into());
    // }
    Ok(())
}

fn finalize_zip(run_state: &mut RunState, files_completed: usize, args: &ZipArgs) -> Result<()> {
    run_state.debug_print("Zipping up new entries");

    if args.basic_options.add_comments {
        log::info!("Adding comments...");
        set_file_comment(run_state)?;
    }

    if args.basic_options.add_archive_comment {
        run_state.debug_print("Get comment if any");
        log::info!("Adding archive comments...");
        let mut writer = run_state.writer.take().expect("Writer not initialized");
        let old_comments = writer.get_archive_comment();
        if !old_comments.is_empty() && !args.basic_options.quiet {
            println!("current zip file comment is: \n{}", old_comments);
        }
        let comments = get_input_archive_comment()?;
        writer.set_archive_comment(&comments);
        run_state.writer = Some(writer);
    } else {
        run_state.debug_print("Get comment if any");
    }

    run_state.debug_print("Writing central directory");
    run_state.debug_print("Writing end of central directory");
    run_state.debug_print("Replacing old zip file");
    run_state.finish()?;
    run_state.debug_print("Setting file type");

    // 删除所有已记录的目录
    for dir in &run_state.dirs_to_remove {
        if dir.exists() && dir.is_dir() {
            // 检查目录是否为空
            let is_empty = fs::read_dir(dir)?.next().is_none();
            if is_empty {
                debug!("Removing directory: {}", dir.display());
                if let Err(e) = fs::remove_dir_all(dir) {
                    warn!("Failed to remove directory {}: {}", dir.display(), e);
                    return Err(anyhow::Error::msg(format!(
                        "Failed to remove directory {}: {}",
                        dir.display(),
                        e
                    )));
                }
            } else {
                warn!("Directory {} is not empty, skipping removal", dir.display());
            }
        }
    }

    if !args.basic_options.quiet {
        debug!(
            "Success! Updated {} with {} new files",
            run_state.zip_file.as_ref().unwrap().display(),
            files_completed
        );
        if args.basic_options.verbose {
            run_state.print_summary_verbose();
        }
    }

    Ok(())
}
